/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package org.apache.hugegraph.api;

import java.util.List;
import java.util.Map;

import jakarta.ws.rs.core.Response;

import org.junit.Assume;
import org.junit.Test;

import org.apache.hugegraph.testutil.Assert;
import com.google.common.collect.ImmutableMap;

public class GremlinApiTest extends BaseApiTest {

    private static String path = "/gremlin";

    @Test
    public void testPost() {
        String body = "{" +
                "\"gremlin\":\"g.V()\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));
    }

    @Test
    public void testGet() {
        Map<String, Object> params = ImmutableMap.of("gremlin",
                                     "hugegraph.traversal().V()");
        Response r = client().get(path, params);
        Assert.assertEquals(r.readEntity(String.class), 200, r.getStatus());
    }

    @Test
    public void testScript() {
        String bodyTemplate = "{" +
                "\"gremlin\":\"%s\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";

        String script = "schema=hugegraph.schema();" +
                "schema.propertyKey('name').asText().ifNotExist().create();" +
                "schema.propertyKey('age').asInt().ifNotExist().create();" +
                "schema.propertyKey('city').asUUID().ifNotExist().create();" +
                "schema.propertyKey('lang').asText().ifNotExist().create();" +
                "schema.propertyKey('date').asText().ifNotExist().create();" +
                "schema.propertyKey('price').asInt().ifNotExist().create();" +
                "person=schema.vertexLabel('person').properties('name','age','city')." +
                "useCustomizeUuidId().ifNotExist().create();" +
                "knows=schema.edgeLabel('knows').sourceLabel('person').targetLabel('person')." +
                "properties('date').ifNotExist().create();" +
                "marko=hugegraph.addVertex(T.id,'835e1153928149578691cf79258e90eb'" +
                ",T.label,'person','name','marko','age',29," +
                "'city','135e1153928149578691cf79258e90eb');" +
                "vadas=hugegraph.addVertex(T.id,'935e1153928149578691cf79258e90eb'" +
                ",T.label,'person','name','vadas','age',27," +
                "'city','235e1153928149578691cf79258e90eb');" +
                "marko.addEdge('knows',vadas,'date','20160110');";
        String body = String.format(bodyTemplate, script);
        assertResponseStatus(200, client().post(path, body));

        String queryV = "g.V()";
        body = String.format(bodyTemplate, queryV);
        assertResponseStatus(200, client().post(path, body));

        String queryE = "g.E()";
        body = String.format(bodyTemplate, queryE);
        assertResponseStatus(200, client().post(path, body));
    }

    @Test
    public void testClearAndInit() {
        String body = "{" +
                "\"gremlin\":\"hugegraph.backendStoreFeatures()" +
                "                       .supportsSharedStorage();\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        String content = assertResponseStatus(200, client().post(path, body));
        Map<?, ?> result = assertJsonContains(content, "result");
        @SuppressWarnings({ "unchecked" })
        Object data = ((List<Object>) assertMapContains(result, "data")).get(0);
        boolean supportsSharedStorage = (boolean) data;
        Assume.assumeTrue("Can't clear non-shared-storage backend",
                          supportsSharedStorage);

        body = "{" +
                "\"gremlin\":\"" +
                "  if (!hugegraph.backendStoreFeatures()" +
                "                .supportsSharedStorage())" +
                "    return;" +
                "  def auth = hugegraph.hugegraph().authManager();" +
                "  def admin = auth.findUser('admin');" +
                "  hugegraph.clearBackend();" +
                "  hugegraph.initBackend();" +
                "  auth.createUser(admin);\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));

        body = "{" +
                "\"gremlin\":\"hugegraph.serverStarted(" +
                "              GlobalMasterInfo.master('server1'))\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));
    }

    @Test
    public void testTruncate() {
        String body = "{" +
                "\"gremlin\":\"try {hugegraph.truncateBackend()} " +
                "catch (UnsupportedOperationException e) {}\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));
    }

    @Test
    public void testSetVertexProperty() {
        String pkPath = "/graphs/hugegraph/schema/propertykeys/";
        // Cardinality single
        String foo = "{" +
                "\"name\": \"foo\"," +
                "\"data_type\": \"TEXT\"," +
                "\"cardinality\": \"SINGLE\"," +
                "\"properties\":[]" +
                "}";
        assertResponseStatus(202, client().post(pkPath, foo));
        // Cardinality list
        String bar = "{" +
                "\"name\": \"bar\"," +
                "\"data_type\": \"TEXT\"," +
                "\"cardinality\": \"LIST\"," +
                "\"properties\":[]" +
                "}";
        assertResponseStatus(202, client().post(pkPath, bar));

        String vlPath = "/graphs/hugegraph/schema/vertexlabels/";
        String vertexLabel = "{" +
                "\"name\": \"person\"," +
                "\"id_strategy\": \"CUSTOMIZE_STRING\"," +
                "\"properties\": [\"foo\", \"bar\"]" +
                "}";
        assertResponseStatus(201, client().post(vlPath, vertexLabel));

        // Not supply cardinality
        String body = "{" +
                "\"gremlin\":\"g.addV('person').property(T.id, '1')" +
                ".property('foo', '123').property('bar', '123')\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));

        // Supply matched cardinality
        body = "{\"gremlin\":\"g.addV('person').property(T.id, '1')" +
                ".property(single, 'foo', '123')" +
                ".property(list, 'bar', '123')\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));

        // Supply unmatch cardinality
        body = "{\"gremlin\":\"g.addV('person').property(T.id, '1')" +
                ".property(list, 'foo', '123')" +
                ".property(list, 'bar', '123')\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(400, client().post(path, body));

        // NOTE: supply unmatch cardinality, but we give up the check
        body = "{\"gremlin\":\"g.addV('person').property(T.id, '1')" +
                ".property(single, 'foo', '123')" +
                ".property(single, 'bar', '123')\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        assertResponseStatus(200, client().post(path, body));
    }

    @Test
    public void testFileSerialize() {
        String body = "{" +
                "\"gremlin\":\"File file = new File('test.text')\"," +
                "\"bindings\":{}," +
                "\"language\":\"gremlin-groovy\"," +
                "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        Response r = client().post(path, body);
        String content = r.readEntity(String.class);
        Assert.assertTrue(content, r.getStatus() == 200);
        Map<?, ?> result = assertJsonContains(content, "result");
        @SuppressWarnings({ "unchecked", "rawtypes" })
        Map data = ((List<Map>) assertMapContains(result, "data")).get(0);
        Assert.assertEquals("test.text", data.get("file"));
    }

    @Test
    public void testVertexOrderByDesc() {
        String body = "{" +
                      "\"gremlin\":\"g.V().order().by(desc)\"," +
                      "\"bindings\":{}," +
                      "\"language\":\"gremlin-groovy\"," +
                      "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        Response response = client().post(path, body);
        assertResponseStatus(200, response);
    }

    @Test
    public void testVertexOrderByAsc() {
        String body = "{" +
                      "\"gremlin\":\"g.V().order().by(asc)\"," +
                      "\"bindings\":{}," +
                      "\"language\":\"gremlin-groovy\"," +
                      "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        Response response = client().post(path, body);
        assertResponseStatus(200, response);
    }

    @Test
    public void testEegeOrderByDesc() {
        String body = "{" +
                      "\"gremlin\":\"g.E().order().by(desc)\"," +
                      "\"bindings\":{}," +
                      "\"language\":\"gremlin-groovy\"," +
                      "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        Response response = client().post(path, body);
        assertResponseStatus(200, response);
    }

    @Test
    public void testEdgeOrderByAsc() {
        String body = "{" +
                      "\"gremlin\":\"g.E().order().by(asc)\"," +
                      "\"bindings\":{}," +
                      "\"language\":\"gremlin-groovy\"," +
                      "\"aliases\":{\"g\":\"__g_hugegraph\"}}";
        Response response = client().post(path, body);
        assertResponseStatus(200, response);
    }
}
